package mx.com.crowdgine.util.xml;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.LinkedList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import mx.com.crowdgine.util.ResourceFinder.ResourceRetriever;

public class XMLReader {
	public static final String ROOT_TAG = "crowd_engine";
	
	public static final String CROWDS_TAG = "crowd";
	public static final String CROWDS_SIZE = "size";
	public static final String CROWDS_GROUPS = "groups";
	public static final String CROWDS_MOVEMENT = "basic_movement";
	public static final String CROWDS_SOCIAL = "social_behavior";
	public static final String CROWDS_DIVERSITY = "diversity";
	
	public static final String TERRAIN_TAG = "terrain";
	public static final String TERRAIN_TYPE = "type";
	public static final String TERRAIN_SCALE = "scale";
	public static final String TERRAIN_SIZE_X = "width";
	public static final String TERRAIN_SIZE_Y = "height";
	public static final String TERRAIN_SIZE_Z = "depth";
	public static final String TERRAIN_ROAD = "road";
	
	public static final String ASSETS_TAG = "asset_loader";
	
	public static final String INPUT_TAG = "input";
	
	public static final String GUI_TAG = "gui";
	
	
	private boolean XMLParsed = false;
	private Node rootNode;
	private HashMap<String, Node> childNodes;
	private LinkedList<String> documentTags;
	
	private boolean verbose = false;

	public XMLReader(String file, boolean... verbose){
		//set debug property
		if(verbose.length > 0){
			this.verbose = verbose[0];
			if(this.verbose)
				System.out.println("Verbose state activated");
		}
		
		InputStream xml = openFile(file);
		
		if(xml != null){
			if(this.verbose)
				System.out.println("XMLReader: File " + file + " opened");
			
			Document doc = parseFile(xml);
			if(doc != null){
				childNodes = buildDocumentHashMap(doc);
				documentTags = buildDocumentList(doc);
				XMLParsed = true;
			}
		}
	}
	
	private InputStream openFile(String file){
		try {
			InputStream xml = ResourceRetriever.getResourceAsStream(file);
			return xml;
		} catch (IOException e) {
			if(this.verbose)
				System.out.println("XMLReader: could not open the file " + file);
			return null;
		}
	}
	
	private Document parseFile(InputStream xml){
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db;
		Document doc;
		
		try {
			db = dbf.newDocumentBuilder();
			doc = db.parse(xml);
			doc.getDocumentElement().normalize();
			return doc;
			
		} catch (SAXException e) {
			System.out.println("XMLReader: SAX exception");
		} catch (IOException e) {
			System.out.println("XMLReader: could not open the stream " + xml);
		}catch (ParserConfigurationException e) {
			System.out.println("XMLReader: parser configuration error");
		}
		return null;
	}
	
	/*private HashMap<String, Node> buildSystemHashMap(Document doc){
		HashMap<String, Node> xmlStructure = new HashMap<String, Node>();
		rootNode = doc.getElementsByTagName(ROOT_TAG).item(0);
		xmlStructure.put(ROOT_TAG, rootNode);
		
		crowd = doc.getElementsByTagName(CROWDS_TAG).item(0);
		xmlStructure.put(CROWDS_TAG, crowd);
		
		return xmlStructure;
	}*/
	
	private LinkedList<String> buildDocumentList(Document doc){
		LinkedList<String> xmlStructure = new LinkedList<String>();
		rootNode = doc.getDocumentElement();
		if(rootNode.getNodeType() == Node.ELEMENT_NODE){
			xmlStructure.add(rootNode.getNodeName());
			if(rootNode.hasChildNodes()){
				NodeList childs = rootNode.getChildNodes();
				for(int i=0; i<childs.getLength(); i++){
					Node child = childs.item(i);
					if(child.getNodeType() == Node.ELEMENT_NODE)
						xmlStructure.add(child.getNodeName());
				}
			}
		}
		return xmlStructure;
	}
	
	private HashMap<String, Node> buildDocumentHashMap(Document doc){
		HashMap<String, Node> xmlStructure = new HashMap<String, Node>();
		rootNode = doc.getDocumentElement();
		if(rootNode.getNodeType() == Node.ELEMENT_NODE){
			xmlStructure.put(rootNode.getNodeName(), rootNode);
			if(rootNode.hasChildNodes()){
				NodeList childs = rootNode.getChildNodes();
				for(int i=0; i<childs.getLength(); i++){
					Node child = childs.item(i);
					if(child.getNodeType() == Node.ELEMENT_NODE)
						xmlStructure.put(child.getNodeName(), child);
				}
			}
		}
		return xmlStructure;
	}
	
	public static LinkedList<String> buildList(Node node){
		LinkedList<String> xmlStructure = new LinkedList<String>();
		if(node.getNodeType() == Node.ELEMENT_NODE){
			if(node.hasChildNodes()){
				NodeList childs = node.getChildNodes();
				for(int i=0; i<childs.getLength(); i++){
					Node child = childs.item(i);
					if(child.getNodeType() == Node.ELEMENT_NODE)
						xmlStructure.add(child.getNodeName());
				}
			}
		}
		return xmlStructure;
	}
	
	public static HashMap<String, Node> buildHashMap(Node node){
		HashMap<String, Node> xmlStructure = new HashMap<String, Node>();
		if(node.getNodeType() == Node.ELEMENT_NODE){
			//xmlStructure.put(node.getNodeName(), node);
			if(node.hasChildNodes()){
				NodeList childs = node.getChildNodes();
				for(int i=0; i<childs.getLength(); i++){
					Node child = childs.item(i);
					if(child.getNodeType() == Node.ELEMENT_NODE)
						xmlStructure.put(child.getNodeName(), child);
				}
			}
		}
		return xmlStructure;
	}
	
	public static HashMap<String, String> getNodeAttributes(Node node){
		HashMap<String, String> nodeAttributes = new HashMap<String, String>();
		if(node.hasAttributes()){
			nodeAttributes.put(node.getNodeName(), null);
			NamedNodeMap attributes = node.getAttributes();
			int length = attributes.getLength();
			for(int i=0; i<length; i++){
				Node att = attributes.item(i);
				nodeAttributes.put(att.getNodeName(), att.getNodeValue());
			}
		}
		return nodeAttributes;
	}
	
	public static LinkedList<HashMap<String, String>> getNodeInformation(Node node){
		LinkedList<HashMap<String, String>> information = new LinkedList<HashMap<String, String>>();
		if(node != null && node.hasChildNodes()){
			if(node.hasAttributes()){
				HashMap<String, String> nodeInfo = getNodeAttributes(node);
				information.add(nodeInfo);
			}
			NodeList groupsChilds = node.getChildNodes();
			int length = groupsChilds.getLength();
			for(int i=0; i<length; i++){
				HashMap<String, String> nodeInfo = null;
				Node child = groupsChilds.item(i);
				short nodeType = child.getNodeType();
				boolean attr = child.hasAttributes();
				if(nodeType == Node.ELEMENT_NODE){
					if(attr){
						nodeInfo = getNodeAttributes(child);
					}else{
						nodeInfo = new HashMap<String, String>();
						nodeInfo.put(child.getNodeName(), null);
					}
				}
				if(nodeInfo != null)
					information.add(nodeInfo);
			}
		}else{
			if(node != null && node.hasAttributes() && node.getNodeType() == Node.ELEMENT_NODE){
				information.add(getNodeAttributes(node));
			}
		}
		
		return information;
	}
	
	public Node getRootNode(){
		return rootNode;
	}
	
	public Node getXMLNode(String tag){
		return childNodes.get(tag);
	}
	
	public boolean tagExists(String tag){
		return documentTags.contains(tag);
	}
	
	public void printNode(Node node){
		if(node != null){
			System.out.println(node.getNodeName());
			if(node.hasChildNodes()){
				NodeList childs = node.getChildNodes();
				for(int i=0; i<childs.getLength(); i++){
					Node child = childs.item(i);
					if(child.getNodeType() == Node.ELEMENT_NODE)
						printNode(child);
				}
			}
		}else{
			System.out.println("XMLReader: the node is not valid");
		}
	}
	
	public void printHashMap(){
		System.out.println(childNodes);
	}
	
	public boolean isXMLParsed(){
		return XMLParsed;
	}
}
